#pragma once

#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <memory.h>
#include <unistd.h>
#include <memory>
#include <sys/wait.h>
#include "log.hpp"

class Sock
{
private:    
    const static int gbacklog = 20;
public:
    Sock(){}
    //1.创建套接字
    int CreateSocket()
    {
        int listensock = socket(AF_INET,SOCK_STREAM,0);
        if(listensock < 0)
        {
            logMessage(FATAL,"crteate socket error,%d-%s",errno,strerror(errno));
            exit(2);
        }
        //获取套接字成功
        logMessage(NORMAL,"create socket success");
        return listensock;
    }
    //2.绑定
    void Bind(int listensock,uint16_t port,std::string ip = "0.0.0.0")
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        inet_pton(AF_INET,ip.c_str(),&local.sin_addr);
        if(bind(listensock,(struct sockaddr*)&local,sizeof local) < 0)
        {
            logMessage(FATAL, "bind error, %d:%s", errno, strerror(errno));
            exit(3);
        }
    }
    //3.监听，等待客户端连接
    void Listen(int listensock)
    {
        if(listen(listensock,gbacklog) < 0)
        {
            logMessage(FATAL, "listen error, %d:%s", errno, strerror(errno));
            exit(4);
        }
        logMessage(NORMAL, "init server success");
    }
    //4.客户端连接后服务端接收
    //const std::string& 输入型参数
    //std::string* 输出型参数（且可以指向nullptr，如果是引用则必须初始化
    //std::string& 输入输出型参数
    int Accept(int listensock, std::string *ip, uint16_t *port)
    {
        struct sockaddr_in rev;
        socklen_t len = sizeof rev;
        int servicesock = accept(listensock,(struct sockaddr*)&rev,&len);
        if(servicesock < 0)
        {
            //连接不上还不至于致命错误，不必退出服务器
            logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno));
            return -1;
        }
        //保证是有地址的，才能访问*ip和*port，比较安全
        if(ip) *ip = inet_ntoa(rev.sin_addr);
        if(port) *port = ntohs(rev.sin_port);
        return servicesock;
    }
    //封装一个客户端使用的Connect
    //为啥要取引用：为了前面加const后左右值都能进入
    bool Connect(int sock,const uint16_t& port,const std::string& ip)
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        inet_pton(AF_INET,ip.c_str(),&local.sin_addr);
        if(connect(sock,(struct sockaddr*)&local,sizeof local) == 0)
            return true;
        else
            return false;
    }
    
    ~Sock(){}
};
